import { ref, reactive, toRaw, computed, watch, type Ref, isRef } from 'vue';
import { Options, Menu, DEFAULT_MENU, Column, Record } from './type';
import Drag from './drag';
import { readTextToClipboard, validatorDecimal } from './util';

export default class extends Drag {
  constructor(options: Options) {
    super(options);

    // 注册菜单
    const menus = toRaw(this.props.menus) || this.getDefaultMenus();

    menus.forEach((menu: Menu | string) => {
      if (!menu) return;
      const key = typeof menu === 'string' ? menu : menu.key ?? this.createId();

      if (!this[key] && key === menu) return;

      // 新菜单
      if (!this[key]) this.registerMenu(key, menu as Menu);
      else this.registerMenu(key, Object.assign(this[key], menu));
    });

    const closeMenus = () => {
      this.closeMenus()
    }
    watch(this.showMenus, (value) => {
      let stopWatch: any;
      if (value) {
        this.resetMenuStatus();
        stopWatch = watch(this.selectedState, (value) => {
          if (value) {
            this.resetMenuStatus();
          }
        });
        document.body.addEventListener('click', closeMenus)
        document.body.addEventListener('wheel', closeMenus)
      } else {
        stopWatch?.()
        document.body.removeEventListener('click', closeMenus)
        document.body.removeEventListener('wheel', closeMenus)
        Promise.resolve().then(() => {
          this.resetMenuStatus();
        })
      }
    });
    
  }

  showMenus = ref(false);
  menuList = ref<Menu[]>([]);
  menuPosition = reactive({
    left: '0px',
    top: '0px',
    transform: 'none'
  });

  openMenus() {
    this.showMenus.value = false;
    if(this.props.menus?.length === 0){
      return;
    }

    // const { left, top } = this.table.value?.getBoundingClientRect?.() ?? {};
    this.menuPosition.left = event.clientX + 'px';
    this.menuPosition.top = event.clientY + 'px';

    const offsetBottom = innerHeight - event.clientY;
    if(offsetBottom - this.menuList.value.length * 30 < 30){
      this.menuPosition.transform = 'translateY(-100%)'
    }else {
      this.menuPosition.transform = 'translateY(0)'
    }

    if (
      this.selectedState.rows || 
      this.selectedState.cells || 
      this.getMaxEndX() - this.getMinStartX() > 0 ||
      (this.getMaxEndY() - this.getMinStartY() > 0 && !this.selectedState.selectedTh)
    ) {
      this.showMenus.value = true;
      return;
    };

    let node: HTMLElement = event?.target;
    while (!['TD', 'TH'].includes(node.tagName)) {
      if (node.tagName === 'TABLE') return;
      node = node.parentElement;
    }
    // 行选择、或者表头行号
    if (node.classList.contains('row-check') || (node.tagName === 'TH' && node.classList.contains('row-index'))) return;

    this.resetSelectedState()
    const { colindex, rowindex } = node.dataset;
    
    if(node.tagName === 'TH'){
      this.selectedState.selectedTh = true;
      this.selectedState.startX = this.selectedState.endX = Number(colindex);
      this.selectedState.startY = -1
      this.selectedState.endY = this.dataSource.value.length - 1
    } else if(node.tagName === 'TD' && node.classList.contains('row-index')){
      this.selectedState.startY = this.selectedState.endY = Number(rowindex);
      this.selectedState.startX = this.selectedState.endX = -1
    } else {
      if (this.props.customEditer?.disabledSelectCell) {
        this.selectedState.startX = this.selectedState.endX = -1;
        this.selectedState.startY = this.selectedState.endY = Number(rowindex);
      } else {
        this.selectedState.startX = this.selectedState.endX = Number(colindex);
        this.selectedState.startY = this.selectedState.endY = Number(rowindex);
      }
    }

    this.showMenus.value = true;
    this.callbackSelected()
  }

  async menuClickHandel(item: Menu){
    const va = await item.vForm?.validateFields?.()
    if(va?.errorFields?.length > 0) throw ''

    const needAddHistoryRecord = this.props.openUndo && ![DEFAULT_MENU.COPY, DEFAULT_MENU.REDO, DEFAULT_MENU.UNDO].includes(item.key)
    if(needAddHistoryRecord && this.emptyHistoryRecord()) this.addHistoryRecord();

    await item.handlerBefore?.(item, this.selectAreasData.value, this);
    await item.handler?.(item, this.selectAreasData.value, this);
    await item.handlerAfter?.(item);
    this.closeMenus();
    if(item.inputProps) {
      item.inputProps.value = item.inputDefaultValue
    }

    if(needAddHistoryRecord) this.addHistoryRecord();
  }

  closeMenus() {
    this.showMenus.value = false;
  }

  /**
   * 自定义菜单、覆盖默认菜单配置
   * @param key 菜单项唯一key
   * @param option 菜单配置
   */
  registerMenu(key: string, option: Menu) {
    const exIndex = this.menuList.value.findIndex((item) => item.key === key);
    if (exIndex > -1) {
      Object.assign(this.menuList.value[exIndex], option);
    } else {
      this.menuList.value.push(option);
    }
  }

  resetMenuStatus() {
    this.menuList.value?.forEach(async (item: Menu) => {

      if(item.inputProps) item.inputProps.value = item.inputDefaultValue;
      item.vForm?.clearValidate?.()

      if (typeof item.disabled !== 'function') return;
      item._disabled = await item.disabled(item, this.selectAreasData.value, this)
    })
  }

  // 检查菜单项是否要将其显示
  visableMenu(menu: Menu){
    if (typeof menu.hide === 'function') return !menu.hide(menu, this.selectAreasData.value, this)
    if (isRef(menu.hide)) return !menu.hide.value
    return !menu.hide;
  }

  private getDefaultMenus(): Menu[] {
    return [
      this[DEFAULT_MENU.COPY],
      this[DEFAULT_MENU.PAETS],
      this[DEFAULT_MENU.COPY_DOWN],
      this[DEFAULT_MENU.ADD_STEP_DOWN],
      this[DEFAULT_MENU.INSERT_BEFORE_ROWS],
      this[DEFAULT_MENU.INSERT_AFTER_ROWS],
      this[DEFAULT_MENU.INSERT_COPY_AFTER_ROWS],
      this[DEFAULT_MENU.UNDO],
      this[DEFAULT_MENU.REDO],
      this[DEFAULT_MENU.DELETE_ROWS],
    ];
  }

  private validatorInputValue(item: Menu){
    const val = item.inputProps?.value
    const precision: number|undefined = item.inputPrecision

    if(precision !== undefined) {
      return validatorDecimal(val, { 
        maxDecimal: precision, 
        message:  precision === 0 ? 
        /*`只能输入整数`*/ this.getMessage('input.mustInt').value : 
        /*`只能输入${precision}位小数`*/ this.getMessage('input.maxDecimal').value.replace?.('[]', `${precision}`)
      })
    }

    return Promise.resolve()
  }

  [DEFAULT_MENU.COPY] = {
    key: DEFAULT_MENU.COPY,
    label: /*复制*/this.getMessage('menus.copyLabel'),
    hide: (menu, { records }) => {
      return this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    disabled: () => {
      const { startY, endY, startX, endX, rows, cells } = this.selectedState
      if(
        startY !== undefined &&
        startY >= 0 && 
        startY === endY && 
        startX !== undefined && 
        startX === endX && 
        startX >= 0
      ){
        return this.getValueByModel(this.columns.value[startX].dataIndex, this.dataSource.value[startY]) === undefined
      }
      return startY === undefined && !rows && !cells;
    },
    handler: () => this.copyHandel(),
  };

  [DEFAULT_MENU.PAETS] = {
    key: DEFAULT_MENU.PAETS,
    label: /*粘贴*/this.getMessage('menus.paetsLabel'),
    hide: (menu, { records }) => {
      return this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    disabled: async () => {
      // const copydata = await readTextToClipboard();
      // if (!copydata) return true;
      return this.selectedState.startY === undefined && !this.selectedState.rows && !this.selectedState.cells
    },
    handler: () => this.pasteHandel(),
  };

  [DEFAULT_MENU.UNDO] = {
    key: DEFAULT_MENU.UNDO,
    label: /*撤销*/this.getMessage('menus.undoLabel'),
    hide: (menu, { records }) => {
      return !this.props.openUndo || this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    disabled: () => !this.can_undo(),
    handler: () => this.undo(),
  };

  [DEFAULT_MENU.REDO] = {
    key: DEFAULT_MENU.REDO,
    label: /*恢复*/this.getMessage('menus.redoLabel'),
    hide: (menu, { records }) => {
      return !this.props.openUndo || this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    disabled: () => !this.can_redo(),
    handler: () => this.redo(),
  };

  [DEFAULT_MENU.COPY_DOWN] = {
    key: DEFAULT_MENU.COPY_DOWN,
    label: /*向下复制*/this.getMessage('menus.copyDownLabel'),
    hide: (menu, { records }) => {
      return this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    disabled: () => {
      const { startY, endY, startX, endX, rows, cells } = this.selectedState
      if (rows || cells) return true
      if (
        startY !== undefined &&
        startY >= 0 && 
        startY === endY && 
        startX !== undefined && 
        startX === endX && 
        startX >= 0
      ) {
        return this.getValueByModel(this.columns.value[startX].dataIndex, this.dataSource.value[startY]) === undefined
      }
      return startY === undefined || startY !== endY;
    },
    handler: () => this.copydownHandel(),
  };

  [DEFAULT_MENU.ADD_STEP_DOWN] = {
    key: DEFAULT_MENU.ADD_STEP_DOWN,
    label: /*'向下填充-递增'*/ this.getMessage('menus.addStepDownLabel'),
    input: true,
    inputDefaultValue: 1,
    inputProps: {
      min: 0,
      max: 999999,
      value: 1,
    },
    hide: (menu, { records }) => {
      return this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    validator: (...arg: any[]) => this.validatorInputValue(...arg),
    disabled: () => {
      return (
        this.selectedState.startY === undefined ||
        this.selectedState.startY !== this.selectedState.endY
      );
    },
    handler: (item: Menu) => {
      this.addStepdownHandel(item.inputProps?.value ?? 1)
    },
  };

  [DEFAULT_MENU.INSERT_BEFORE_ROWS] = {
    key: DEFAULT_MENU.INSERT_BEFORE_ROWS,
    label: /*'上方插入[input]行'*/this.getMessage('menus.insertBeforeLabel'),
    input: true,
    inputDefaultValue: 1,
    inputPrecision: 0,
    validator: (...arg: any[]) => this.validatorInputValue(...arg),
    inputProps: {
      min: 0,
      max: 999999,
      value: 1,
    },
    hide: (menu, { records }) => {
      return this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    disabled: () =>  this.selectedState.startY === undefined,
    handler: (item: Menu) => {
      if (this.getMinStartY() !== undefined) {
        this.insertRows(this.getMinStartY(), 'before',  item.inputProps?.value ?? 1);
      }
    },
  };

  [DEFAULT_MENU.INSERT_AFTER_ROWS] = {
    key: DEFAULT_MENU.INSERT_AFTER_ROWS,
    label: /*'下方插入[input]行'*/  this.getMessage('menus.insertAfterLabel'),
    input: true,
    inputDefaultValue: 1,
    inputPrecision: 0,
    validator: (...arg: any[]) => this.validatorInputValue(...arg),
    inputProps: {
      min: 0,
      max: 999999,
      value: 1,
    },
    hide: (menu, { records }) => {
      return this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    disabled: () =>  this.selectedState.startY === undefined,
    handler: (item: Menu) => {
      if (this.getMaxEndY() !== undefined) {
        this.insertRows(this.getMaxEndY(), 'after',  item.inputProps?.value ?? 1);
      }
    },
  };

  [DEFAULT_MENU.INSERT_COPY_AFTER_ROWS] = {
    key: DEFAULT_MENU.INSERT_COPY_AFTER_ROWS,
    label: /*'向下复制[input]行'*/ this.getMessage('menus.insertAfterCopyLabel'),
    input: true,
    inputDefaultValue: 1,
    inputPrecision: 0,
    validator: (...arg: any[]) => this.validatorInputValue(...arg),
    inputProps: {
      min: 0,
      max: 999999,
      value: 1,
    },
    hide: (menu, { records }) => {
      return this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    disabled: () => this.getMaxEndY() === undefined || this.getMinStartY() !== this.getMaxEndY(),
    handler: (item: Menu) => {
      if (this.getMaxEndY() !== undefined) {
        this.insertCopyRows(this.getMaxEndY(), 'after', item.inputProps?.value ?? 1, [
          this.dataSource.value[this.getMaxEndY()],
        ]);
      }
    },
  };

  [DEFAULT_MENU.DELETE_ROWS] = {
    key: DEFAULT_MENU.DELETE_ROWS,
    label: /*'删除选中行'*/  this.getMessage('menus.deleteRowsLabel'),
    hide: (menu, { records }) => {
      return this.checkHasReadonly(records) || this.checkHasDisable(records)
    },
    disabled: () =>  {
      if (this.props.keepLastRowEmpty && this.selectedState.endY === this.dataSource.value.length - 1) return true;
      return this.selectedState.startY === undefined && !this.selectedState.rows
    },
    handler: () => this.deleteRows(),
  };

  private $$getMinStartX() {
    if (this.selectedState.startX === -1) return 0;
    return Math.min(
      this.selectedState.startX ?? 0,
      this.selectedState.endX ?? 0,
    );
  }
  private $$getMaxEndX() {
    if (this.selectedState.startX === -1) return this.columns.value.length - 1;
    return Math.max(
      this.selectedState.startX ?? 0,
      this.selectedState.endX ?? 0,
    );
  }
  private $$getChoseColumns() {
    return this.columns.value.slice(
      this.$$getMinStartX(),
      this.$$getMaxEndX() + 1,
    );
  }
}
